package edu.maplef.bookwild.utils.network;

import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.AccessControlException;
import java.util.HashMap;
import java.util.Map;

import edu.maplef.bookwild.utils.StrCharUtil;

/**
 * A utility class to handle HTTP request/response.
 * @author maple
 */
public class HttpClient implements java.io.Serializable {
	private static final int OK = 200;// OK: Success!

	private String basic;
	private static final long serialVersionUID = 808018030183407996L;
	private static boolean isJDK14orEarlier = false;
	private Map<String, String> requestHeaders = new HashMap<String, String>();
	public int retryCount = 5;
	public int retryIntervalMillis = 10000;

	private String token = null;

	static {
		try {
			String versionStr = System
					.getProperty("java.specification.version");
			if (null != versionStr) {
				isJDK14orEarlier = 1.5d > Double.parseDouble(versionStr);
			}
		} catch (AccessControlException ace) {
			isJDK14orEarlier = true;
		}
	}

	public HttpClient() {
		this.basic = null;
		setRequestHeader("Accept-Encoding", "gzip");
	}

	protected Response httpRequest(String url, PostParameter[] postParams)
			throws Exception {
		int len = 1;
		String method = "GET";
		if (postParams != null) {
			method = "POST";
			len = postParams.length + 1;
		}
		return httpRequest(url, postParams, method);
	}

	public Response httpRequest(String url, PostParameter[] postParams,
			String httpMethod) throws Exception {
		int retriedCount;
		int retry = retryCount + 1;
		Response res = null;
		for (retriedCount = 0; retriedCount < retry; retriedCount++) {
			int responseCode = -1;
			try {
				HttpURLConnection con = null;
				OutputStream osw = null;
				try {
					con = getConnection(url);
					con.setDoInput(true);
					setHeaders(url, postParams, con, httpMethod);
					if (null != postParams || "POST".equals(httpMethod)) {
						con.setRequestMethod("POST");
						con.setRequestProperty("Content-Type",
								"application/x-www-form-urlencoded");
						con.setDoOutput(true);
						String postParam = "";
						if (postParams != null) {
							postParam = encodeParameters(postParams);
						}
						byte[] bytes = postParam.getBytes("UTF-8");

						con.setRequestProperty("Content-Length", Integer
								.toString(bytes.length));
						osw = con.getOutputStream();
						osw.write(bytes);
						osw.flush();
						osw.close();
					} else if ("DELETE".equals(httpMethod)) {
						con.setRequestMethod("DELETE");
					} else {
						con.setRequestMethod("GET");
					}
					res = new Response(con);
					responseCode = con.getResponseCode();
					if (responseCode != OK) {
						throw new Exception("server returns" + responseCode);
					}

				} finally {
					try {
						osw.close();
					} catch (Exception ignore) {
					}
				}
			} catch (IOException ioe) {
				// connection timeout or read timeout
				if (retriedCount == retryCount) {
					throw new Exception("reach the limit of tries");
				}
			}
			try {
				Thread.sleep(retryIntervalMillis);
			} catch (InterruptedException ignore) {
				//nothing to do
			}
		}
		return res;
	}

	public static String encodeParameters(PostParameter[] postParams) {
		StringBuffer buf = new StringBuffer();
		for (int j = 0; j < postParams.length; j++) {
			if (j != 0) {
				buf.append("&");
			}
			try {
				buf
						.append(URLEncoder.encode(postParams[j].name, "UTF-8"))
						.append("=")
						.append(URLEncoder.encode(postParams[j].value, "UTF-8"));
			} catch (java.io.UnsupportedEncodingException neverHappen) {
			}
		}
		return buf.toString();

	}

	/**
	 * sets HTTP headers
	 *
	 * @param connection    HttpURLConnection
	 * @param authenticated boolean
	 */
	private void setHeaders(String url, PostParameter[] params,
			HttpURLConnection connection, String httpMethod) {
		for (String key : requestHeaders.keySet()) {
			connection.addRequestProperty(key, requestHeaders.get(key));
		}
	}

	public void setRequestHeader(String name, String value) {
		requestHeaders.put(name, value);
	}

	public String getRequestHeader(String name) {
		return requestHeaders.get(name);
	}

	private HttpURLConnection getConnection(String url) throws IOException {
		HttpURLConnection con = null;
		con = (HttpURLConnection) new URL(url).openConnection();
		con.setConnectTimeout(20000);
		con.setReadTimeout(120000);
		return con;
	}

	/**
	 * 根据原始URL和参数表返回生成的URL（供浏览器访问）
	 * @param url
	 * @param params
	 * @return
	 */
	public static String BuildUrl(String url, PostParameter[] params) {
		String postParam = encodeParameters(params);
		if (!url.endsWith("?"))
			url = url + "?" + postParam;
		else
			url = url + postParam;
		url = StrCharUtil.URLEncode(url, "UTF-8");
		return url;
	}

}
